package com.example.sppmode.DataProtocol;

import android.util.Log;

import com.example.sppmode.Common.MyByteQueue;
import com.example.sppmode.DataProtocol.CommandDevInfo;
import com.example.sppmode.DataProtocol.CommandImageInfo;
import com.example.sppmode.DataProtocol.CommandInfo;
import com.example.sppmode.Common.Utils;

import java.util.Locale;

public class CommandDataAnalyze {

    private static final String TAG = "DataAnalyze";

    public static final int FRAME_HEAD_FLAG_LEN = 1;
    public static final int FRAME_HEAD_FROMTO_LEN = 1;
    public static final int FRAME_HEAD_PATH_LEN = 1;
    public static final int FRAME_HEAD_CMDID_LEN = 1;
    public static final int FRAME_HEAD_DATA_LEN = 2;
    public static final int FRAME_HEAD_LEN = (FRAME_HEAD_FLAG_LEN + FRAME_HEAD_FROMTO_LEN + FRAME_HEAD_PATH_LEN + FRAME_HEAD_CMDID_LEN + FRAME_HEAD_DATA_LEN);
    public static final int FRAME_CRC_LEN = (2);
    public static final int CMD_DATA_MAX_LEN = (4096 + 20);
    public static final int FRAME_MIN_LEN = (FRAME_HEAD_LEN + FRAME_CRC_LEN);
    public static final int FRAME_MAX_LEN = (CMD_DATA_MAX_LEN + FRAME_MIN_LEN);

    public static final byte FRAME_HEAD = (byte) 0xFE;
    public static final int CRC16_INIT = 0xA1B2;

    public static byte[] packFrameData(CommandInfo cmdInfo) {
        if (cmdInfo == null) {
            return null;
        }
        if (cmdInfo.getCmdData() == null) {
            return null;
        }

        if (cmdInfo.getCmdData().length > CMD_DATA_MAX_LEN) {
            return null;
        }

        byte[] cmdData = cmdInfo.getCmdData();
        int cmdDataLen = cmdInfo.getCmdDataLen();
        byte[] frameData = new byte[FRAME_HEAD_LEN + cmdDataLen + 2];

        int index = 0;
        frameData[index++] = FRAME_HEAD;
        frameData[index++] = (byte) cmdInfo.getFrom();
        frameData[index++] = (byte) cmdInfo.getTo();
        frameData[index++] = (byte) cmdInfo.getCommandID();
        frameData[index++] = (byte) ((cmdDataLen >> 8) & 0xff);
        frameData[index++] = (byte) (cmdDataLen & 0xff);

        for (int i = 0; i < cmdDataLen; i++) {
            frameData[index++] = cmdData[i];
        }

        int crc16 = Utils.calcCrc16(CRC16_INIT, frameData, 0, FRAME_HEAD_LEN + cmdDataLen - 1);
        frameData[index++] = (byte) ((crc16 >> 8) & 0xff);
        frameData[index++] = (byte) ((crc16) & 0xff);

        Log.d(TAG, "packFrameData frameLen = " + index);
        Log.d(TAG, "packFrameData frameData = " + Utils.convertByteArray2HexStr(frameData, index));
        return frameData;
    }

    public static boolean validCommandId(int cmdId) {
        for (int id : CommandInfo.commands) {
            if (id == cmdId)
                return true;
        }
        return false;
    }

    public static CommandAnalyzeResult analyzeFrameData(MyByteQueue recvQueue) {
        CommandAnalyzeResult analyzeResult = new CommandAnalyzeResult();

        if (recvQueue == null) {
            analyzeResult.setErrCode(CommandInfo.ERROR_CODE_NULL_POINTER);
            return analyzeResult;
        }

        if (recvQueue.length() < FRAME_MIN_LEN) {
            recvQueue.clear();
            analyzeResult.setErrCode(CommandInfo.ERROR_CODE_INVALID_FRAME_LEN);
            return analyzeResult;
        }

        byte[] frameHead = recvQueue.peekQueue(FRAME_HEAD_LEN);

        Log.d(TAG, "analyzeFrameData frameHead=" + Utils.convertByteArray2HexStr(frameHead, frameHead.length));

        if (frameHead[0] != FRAME_HEAD) {
            Log.d(TAG, "analyzeFrameData framehead invaild");

            /* 帧头标志不正确，清空接收数据。 */
            recvQueue.clear();
            analyzeResult.setErrCode(CommandInfo.ERROR_CODE_INVALID_HEAD_FLAG);
            return analyzeResult;
        }

        CommandInfo commandInfo = new CommandInfo();
        int from = frameHead[1] & 0xff;
        int to = frameHead[2] & 0xff;
        int commandID = frameHead[3] & 0xff;
        int cmdDataLen = ((frameHead[4] & 0xff) << 8) | (frameHead[5] & 0xff);

        if (from != CommandInfo.DEVICE_LEFT && from != CommandInfo.DEVICE_RIGHT && from != CommandInfo.DEVICE_MCU) {
            Log.d(TAG, "analyzeFrameData from(" + from + ") invaild");
            recvQueue.clear();
            analyzeResult.setErrCode(CommandInfo.ERROR_CODE_INVALID_FROM);
            return analyzeResult;
        }

        if (to != CommandInfo.DEVICE_APP) {
            Log.d(TAG, "analyzeFrameData to(" + to + ") invaild");
            recvQueue.clear();
            analyzeResult.setErrCode(CommandInfo.ERROR_CODE_INVALID_TO);
            return analyzeResult;
        }

        if (!validCommandId(commandID)) {
            Log.d(TAG, "analyzeFrameData commandID(" + commandID + ") invaild");
            recvQueue.clear();
            analyzeResult.setErrCode(CommandInfo.ERROR_CODE_INVALID_CMDID);
            return analyzeResult;
        }

        if (cmdDataLen > CMD_DATA_MAX_LEN) {
            Log.d(TAG, "analyzeFrameData cmdDataLen(" + cmdDataLen + ") > " + CMD_DATA_MAX_LEN + ") invaild");
            recvQueue.clear();
            analyzeResult.setErrCode(CommandInfo.ERROR_CODE_INVALID_DATA_LEN);
            return analyzeResult;
        }

        int frameLen = FRAME_HEAD_LEN + cmdDataLen + FRAME_CRC_LEN;
        Log.d(TAG, "analyzeFrameData recvQueue.length=" + recvQueue.length() + ", frameLen=" + frameLen);
        if (recvQueue.length() < frameLen) {
            Log.d(TAG, "analyzeFrameData waiting more data, " +
                    "frameLen(" + frameLen + ") < FRAME_HEAD_LEN(" + FRAME_HEAD_LEN + ") + cmdDataLen(" + cmdDataLen + ") + FRAME_CRC_LEN(" + FRAME_CRC_LEN + ")");
            analyzeResult.setErrCode(CommandInfo.ERROR_CODE_INVALID_WAIT_MORE);
            return analyzeResult;
        }

        byte[] frameData = recvQueue.deQueue(frameLen);
        int recv_crc = ((frameData[frameLen - 2] & 0xff) << 8) | (frameData[frameLen - 1] & 0xff);
        int calc_crc = Utils.calcCrc16(CRC16_INIT, frameData, 0, FRAME_HEAD_LEN + cmdDataLen - 1);
        if (recv_crc != calc_crc) {
            Log.d(TAG, "analyzeFrameData recv_crc(" + recv_crc + ") != calc_crc(" + calc_crc + ")");
            recvQueue.clear();
            analyzeResult.setErrCode(CommandInfo.ERROR_CODE_INVALID_CRC);
            return analyzeResult;
        }

        byte[] cmdData = new byte[cmdDataLen];
        System.arraycopy(frameData, FRAME_HEAD_LEN, cmdData, 0, cmdDataLen);
        commandInfo.setFrom(from);
        commandInfo.setTo(to);
        commandInfo.setCommandID(commandID);
        commandInfo.setCmdData(cmdData);
        commandInfo.setFrameData(frameData);
        String str = String.format(Locale.getDefault(), "fromTo=%02X, path=%02X, commandID=%02X, cmdDataLen=%d", from, to, commandID, cmdDataLen);
        Log.d(TAG, "analyzeFrameData success, " + str);

        analyzeResult.setErrCode(CommandInfo.ERROR_CODE_INVALID_CRC);
        analyzeResult.setCommandInfo(commandInfo);
        return analyzeResult;
    }

    public static void dealCommandData(CommandInfo commandInfo) {
        byte[] cmdData = commandInfo.getCmdData();
        int index = 0;
        int errCode;
        int earSide = commandInfo.getEarSide();

        switch (commandInfo.getCommandID()) {
            case CommandInfo.CMDID_DATA_ANALYZE_ERROR:
                Log.d(TAG, "dealCommandData CMDID_DATA_ANALYZE_ERROR");
                errCode = (int) cmdData[index];
                commandInfo.setErrCode(errCode);
                break;

            case CommandInfo.CMDID_QUERY_DEV_FW_VER:
                Log.d(TAG, "dealCommandData CMDID_QUERY_DEV_FW_VER");
                errCode = (int) cmdData[index++];
                commandInfo.setErrCode(errCode);
                if (errCode == 0x00) {
                    String fwVer = String.format(Locale.getDefault(), "%d.%d.%d.%d", cmdData[0], cmdData[1], cmdData[2], cmdData[3]);

                    CommandDevInfo devInfo = new CommandDevInfo();
                    devInfo.setFwVerion(fwVer);
                    commandInfo.setCommandDevInfo(devInfo);
                }
                break;

            case CommandInfo.CMDID_BAT_INFO_SYNC:
                Log.d(TAG, "dealCommandData CMDID_BAT_INFO_SYNC");
                errCode = (int) 0x00;
                commandInfo.setErrCode(errCode);
                if (cmdData.length == 4) {
                    int chg_sta = (int) cmdData[index++];
                    int bat_voltage = (int) ((cmdData[index++] & 0xff) << 8);
                    bat_voltage |= (int) (cmdData[index++] & 0xff);
                    int bat_percent = (int) cmdData[index++];

                    commandInfo.setBatteryPercent(bat_percent);
                }
                break;

            case CommandInfo.CMDID_RECOGNIZED_TAG_REPORT:
                Log.d(TAG, "dealCommandData CMDID_RECOGNIZED_TAG_REPORT");
                errCode = (int) cmdData[index++];
                commandInfo.setErrCode(errCode);
                if (errCode == 0x00) {
                    int percent = (int) cmdData[index++];
                    int tag = (int) ((cmdData[index++] & 0xff) << 8);
                    tag |= (int) (cmdData[index++] & 0xff);

                    commandInfo.setBatteryPercent(percent);
                    commandInfo.setRecognizedTag(tag);
                }
                break;

            case CommandInfo.CMDID_KEY_VALUE_ISSUE:
                Log.d(TAG, "dealCommandData CMDID_KEY_VALUE_ISSUE");
                errCode = (int) cmdData[index++];
                commandInfo.setErrCode(errCode);
                break;

            case CommandInfo.CMDID_IMAGE_REPORT_IMAGE_INFO:
                Log.d(TAG, "dealCommandData CMDID_IMAGE_REPORT_IMAGE_INFO");
                errCode = 0;
                commandInfo.setErrCode(errCode);
                index = 0;
                if (errCode == 0x00) {
                    int imgWidht = ((cmdData[index++] & 0xff) << 8);
                    imgWidht |= cmdData[index++] & 0xff;

                    int imgHeight = (cmdData[index++] & 0xff) << 8;
                    imgHeight |= cmdData[index++] & 0xff;

                    int imgSize = (cmdData[index++] & 0xff) << 24;
                    imgSize |= (cmdData[index++] & 0xff) << 16;
                    imgSize |= (cmdData[index++] & 0xff) << 8;
                    imgSize |= cmdData[index++] & 0xff;

                    int imgFormat = cmdData[index++] & 0xff;

                    int imgPackCnt = (cmdData[index++] & 0xff) << 8;
                    imgPackCnt |= cmdData[index] & 0xff;

                    CommandImageInfo commandImageInfo = new CommandImageInfo();

                    commandImageInfo.setImageWidth(imgWidht);
                    commandImageInfo.setImageHeight(imgHeight);
                    commandImageInfo.setImageSize(imgSize);
                    commandImageInfo.setImageFormat(imgFormat);
                    commandImageInfo.setImagePackCnt(imgPackCnt);

                    commandInfo.setCommandImageInfo(commandImageInfo);
                }
                break;
            case CommandInfo.CMDID_IMAGE_REPORT_IMAGE_DATA:
                Log.d(TAG, "dealCommandData CMDID_IMAGE_REPORT_IMAGE_DATA");
                errCode = 0;
                commandInfo.setErrCode(errCode);
                index = 0;
                if (errCode == 0x00) {
                    int imgPackIndex = ((cmdData[index++] & 0xff) << 8);
                    imgPackIndex |= cmdData[index++] & 0xff;

                    int imgPackSize = (cmdData[index++] & 0xff) << 8;
                    imgPackSize |= cmdData[index++] & 0xff;


                    byte[] imgPackData = null;
                    if (imgPackSize > 0 && imgPackSize <= 1024) {
                        imgPackData = new byte[imgPackSize];
                        System.arraycopy(cmdData, index, imgPackData, 0, imgPackSize);
                    }

                    CommandImageInfo commandImageInfo = new CommandImageInfo();
                    commandImageInfo.setImagePackIndex(imgPackIndex);
                    commandImageInfo.setImagePackSize(imgPackSize);
                    commandImageInfo.setImagePackData(imgPackData);
                    commandInfo.setCommandImageInfo(commandImageInfo);
                }
                break;

            default:
                break;
        }
    }
}
